/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mule.devkit.doclet; import com.sun.javadoc.AnnotationDesc; import com.sun.javadoc.AnnotationTypeDoc; import com.sun.javadoc.AnnotationTypeElementDoc; import com.sun.javadoc.AnnotationValue; import com.sun.javadoc.ClassDoc; import com.sun.javadoc.ConstructorDoc; import com.sun.javadoc.ExecutableMemberDoc; import com.sun.javadoc.FieldDoc; import com.sun.javadoc.MethodDoc; import com.sun.javadoc.PackageDoc; import com.sun.javadoc.ParamTag; import com.sun.javadoc.Parameter; import com.sun.javadoc.RootDoc; import com.sun.javadoc.SeeTag; import com.sun.javadoc.SourcePosition; import com.sun.javadoc.Tag; import com.sun.javadoc.ThrowsTag; import com.sun.javadoc.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; public class Converter { private static RootDoc root; public static void makeInfo(RootDoc r) { root = r; int N, i; // create the objects ClassDoc[] classDocs = r.classes(); N = classDocs.length; for (i = 0; i < N; i++) { Converter.obtainClass(classDocs[i]); } ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>(); // fill in the fields that reference other classes while (mClassesNeedingInit.size() > 0) { i = mClassesNeedingInit.size() - 1; ClassNeedingInit clni = mClassesNeedingInit.get(i); mClassesNeedingInit.remove(i); initClass(clni.c, clni.cl); classesNeedingInit2.add(clni.cl); } mClassesNeedingInit = null; for (ClassInfo cl : classesNeedingInit2) { cl.init2(); } finishAnnotationValueInit(); // fill in the "root" stuff mRootClasses = Converter.convertClasses(r.classes()); } private static ClassInfo[] mRootClasses; public static ClassInfo[] rootClasses() { return mRootClasses; } public static ClassInfo[] allClasses() { return (ClassInfo[]) mClasses.all(); } private static void initClass(ClassDoc c, ClassInfo cl) { MethodDoc[] annotationElements; if (c instanceof AnnotationTypeDoc) { annotationElements = ((AnnotationTypeDoc) c).elements(); } else { annotationElements = new MethodDoc[0]; } cl.init(Converter.obtainType(c), Converter.convertClasses(c.interfaces()), Converter .convertTypes(c.interfaceTypes()), Converter.convertClasses(c.innerClasses()), Converter .convertMethods(c.constructors(false)), Converter.convertMethods(c.methods(false)), Converter.convertMethods(annotationElements), Converter.convertFields(c.fields(false)), Converter.convertFields(c.enumConstants()), Converter.obtainPackage(c.containingPackage()), Converter.obtainClass(c.containingClass()), Converter.obtainClass(c.superclass()), Converter.obtainType(c.superclassType()), Converter.convertAnnotationInstances(c .annotations())); cl.setHiddenMethods(Converter.getHiddenMethods(c.methods(false))); cl.setNonWrittenConstructors(Converter.convertNonWrittenConstructors(c.constructors(false))); cl.init3(Converter.convertTypes(c.typeParameters()), Converter.convertClasses(c .innerClasses(false))); } public static ClassInfo obtainClass(String className) { return Converter.obtainClass(root.classNamed(className)); } public static PackageInfo obtainPackage(String packageName) { return Converter.obtainPackage(root.packageNamed(packageName)); } private static TagInfo convertTag(Tag tag) { return new TextTagInfo(tag.name(), tag.kind(), tag.text(), Converter.convertSourcePosition(tag .position())); } private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag, ContainerInfo base) { return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(), Converter.obtainClass(tag .exception()), tag.exceptionComment(), base, Converter .convertSourcePosition(tag.position())); } private static ParamTagInfo convertParamTag(ParamTag tag, ContainerInfo base) { return new ParamTagInfo(tag.name(), tag.kind(), tag.text(), tag.isTypeParameter(), tag .parameterComment(), tag.parameterName(), base, Converter.convertSourcePosition(tag .position())); } private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base) { return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base, Converter .convertSourcePosition(tag.position())); } private static SourcePositionInfo convertSourcePosition(SourcePosition sp) { if (sp == null) { return null; } return new SourcePositionInfo(sp.file().toString(), sp.line(), sp.column()); } public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base) { int len = tags.length; TagInfo[] out = new TagInfo[len]; for (int i = 0; i < len; i++) { Tag t = tags[i]; /* * System.out.println("Tag name='" + t.name() + "' kind='" + t.kind() + "'"); */ if (t instanceof SeeTag) { out[i] = Converter.convertSeeTag((SeeTag) t, base); } else if (t instanceof ThrowsTag) { out[i] = Converter.convertThrowsTag((ThrowsTag) t, base); } else if (t instanceof ParamTag) { out[i] = Converter.convertParamTag((ParamTag) t, base); } else { out[i] = Converter.convertTag(t); } } return out; } public static ClassInfo[] convertClasses(ClassDoc[] classes) { if (classes == null) { return null; } int N = classes.length; ClassInfo[] result = new ClassInfo[N]; for (int i = 0; i < N; i++) { result[i] = Converter.obtainClass(classes[i]); } return result; } private static ParameterInfo convertParameter(Parameter p, SourcePosition pos, boolean isVarArg) { if (p == null) { return null; } ParameterInfo pi = new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg, Converter.convertSourcePosition(pos), Converter.convertAnnotationInstances(p.annotations())); return pi; } private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) { SourcePosition pos = m.position(); int len = p.length; ParameterInfo[] q = new ParameterInfo[len]; for (int i = 0; i < len; i++) { boolean isVarArg = (m.isVarArgs() && i == len - 1); q[i] = Converter.convertParameter(p[i], pos, isVarArg); } return q; } private static TypeInfo[] convertTypes(Type[] p) { if (p == null) { return null; } int len = p.length; TypeInfo[] q = new TypeInfo[len]; for (int i = 0; i < len; i++) { q[i] = Converter.obtainType(p[i]); } return q; } private Converter() { } private static class ClassNeedingInit { ClassNeedingInit(ClassDoc c, ClassInfo cl) { this.c = c; this.cl = cl; } ClassDoc c; ClassInfo cl; } private static ArrayList<ClassNeedingInit> mClassesNeedingInit = new ArrayList<ClassNeedingInit>(); static ClassInfo obtainClass(ClassDoc o) { return mClasses.obtain(o); } private static Cache<ClassDoc, ClassInfo> mClasses = new Cache<ClassDoc, ClassInfo>() { @Override protected ClassInfo make(ClassDoc input) { // A bug while generating OpenJDK documentation reports some classes with no name. if (input.name() == null || input.name().equals("")) { return null; } try { ClassInfo cl = new ClassInfo(input, input.getRawCommentText(), Converter.convertSourcePosition(input.position()), input .isPublic(), input.isProtected(), input.isPackagePrivate(), input.isPrivate(), input.isStatic(), input .isInterface(), input.isAbstract(), input.isOrdinaryClass(), input.isException(), input.isError(), input .isEnum(), (input instanceof AnnotationTypeDoc), input.isFinal(), input.isIncluded(), input.name(), input .qualifiedName(), input.qualifiedTypeName(), input.isPrimitive()); if (mClassesNeedingInit != null) { mClassesNeedingInit.add(new ClassNeedingInit(input, cl)); } return cl; } catch (Exception e) { } return null; } @Override protected void made(ClassDoc input, ClassInfo output) { if (mClassesNeedingInit == null) { initClass(input, output); output.init2(); } } @Override ClassInfo[] all() { return mCache.values().toArray(new ClassInfo[mCache.size()]); } }; private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) { if (methods == null) { return null; } ArrayList<MethodInfo> out = new ArrayList<MethodInfo>(); int N = methods.length; for (int i = 0; i < N; i++) { MethodInfo m = Converter.obtainMethod(methods[i]); // System.out.println(m.toString() + ": "); // for (TypeInfo ti : m.getTypeParameters()){ // if (ti.asClassInfo() != null){ // System.out.println(" " +ti.asClassInfo().toString()); // } else { // System.out.println(" null"); // } // } if (m.isHidden()) { out.add(m); } } return out.toArray(new MethodInfo[out.size()]); } /** * Convert MethodDoc[] into MethodInfo[]. Also filters according to the -private, -public option, * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call. */ private static MethodInfo[] convertMethods(MethodDoc[] methods) { if (methods == null) { return null; } ArrayList<MethodInfo> out = new ArrayList<MethodInfo>(); int N = methods.length; for (int i = 0; i < N; i++) { MethodInfo m = Converter.obtainMethod(methods[i]); // System.out.println(m.toString() + ": "); // for (TypeInfo ti : m.getTypeParameters()){ // if (ti.asClassInfo() != null){ // System.out.println(" " +ti.asClassInfo().toString()); // } else { // System.out.println(" null"); // } // } if (m.checkLevel()) { out.add(m); } } return out.toArray(new MethodInfo[out.size()]); } private static MethodInfo[] convertMethods(ConstructorDoc[] methods) { if (methods == null) { return null; } ArrayList<MethodInfo> out = new ArrayList<MethodInfo>(); int N = methods.length; for (int i = 0; i < N; i++) { MethodInfo m = Converter.obtainMethod(methods[i]); if (m.checkLevel()) { out.add(m); } } return out.toArray(new MethodInfo[out.size()]); } private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) { if (methods == null) { return null; } ArrayList<MethodInfo> out = new ArrayList<MethodInfo>(); int N = methods.length; for (int i = 0; i < N; i++) { MethodInfo m = Converter.obtainMethod(methods[i]); if (!m.checkLevel()) { out.add(m); } } return out.toArray(new MethodInfo[out.size()]); } private static MethodInfo obtainMethod(MethodDoc o) { return mMethods.obtain(o); } private static MethodInfo obtainMethod(ConstructorDoc o) { return mMethods.obtain(o); } private static Cache<ExecutableMemberDoc, MethodInfo> mMethods = new Cache<ExecutableMemberDoc, MethodInfo>() { @Override protected MethodInfo make(ExecutableMemberDoc o) { if (o instanceof AnnotationTypeElementDoc) { AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o; MethodInfo result = new MethodInfo(m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), m .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), m .isAbstract(), m.isSynchronized(), m.isNative(), true, "annotationElement", m .flatSignature(), Converter.obtainMethod(m.overriddenMethod()), Converter .obtainType(m.returnType()), Converter.convertParameters(m.parameters(), m), Converter.convertClasses(m.thrownExceptions()), Converter.convertSourcePosition(m .position()), Converter.convertAnnotationInstances(m.annotations())); result.setVarargs(m.isVarArgs()); result.init(Converter.obtainAnnotationValue(m.defaultValue(), result)); return result; } else if (o instanceof MethodDoc) { MethodDoc m = (MethodDoc) o; MethodInfo result = new MethodInfo(m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), m .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), m .isAbstract(), m.isSynchronized(), m.isNative(), false, "method", m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()), Converter .obtainType(m.returnType()), Converter.convertParameters(m.parameters(), m), Converter.convertClasses(m.thrownExceptions()), Converter.convertSourcePosition(m .position()), Converter.convertAnnotationInstances(m.annotations())); result.setVarargs(m.isVarArgs()); result.init(null); return result; } else { ConstructorDoc m = (ConstructorDoc) o; MethodInfo result = new MethodInfo(m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), m .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), false, m.isSynchronized(), m.isNative(), false, "constructor", m.flatSignature(), null, null, Converter.convertParameters(m.parameters(), m), Converter .convertClasses(m.thrownExceptions()), Converter.convertSourcePosition(m .position()), Converter.convertAnnotationInstances(m.annotations())); result.setVarargs(m.isVarArgs()); result.init(null); return result; } } }; private static FieldInfo[] convertFields(FieldDoc[] fields) { if (fields == null) { return null; } ArrayList<FieldInfo> out = new ArrayList<FieldInfo>(); int N = fields.length; for (int i = 0; i < N; i++) { FieldInfo f = Converter.obtainField(fields[i]); //if (f.checkLevel()) { out.add(f); //} } return out.toArray(new FieldInfo[out.size()]); } private static FieldInfo obtainField(FieldDoc o) { return mFields.obtain(o); } private static Cache<FieldDoc, FieldInfo> mFields = new Cache<FieldDoc, FieldInfo>() { @Override protected FieldInfo make(FieldDoc f) { return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(), f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(), f.constantValue(), Converter.convertSourcePosition(f.position()), Converter .convertAnnotationInstances(f.annotations())); } }; private static PackageInfo obtainPackage(PackageDoc o) { return mPackages.obtain(o); } private static Cache<PackageDoc, PackageInfo> mPackages = new Cache<PackageDoc, PackageInfo>() { @Override protected PackageInfo make(PackageDoc p) { return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position())); } }; private static TypeInfo obtainType(Type o) { return mTypes.obtain(o); } private static Cache<Type, TypeInfo> mTypes = new Cache<Type, TypeInfo>() { @Override protected TypeInfo make(Type t) { String simpleTypeName; if (t instanceof ClassDoc) { simpleTypeName = ((ClassDoc) t).name(); } else { simpleTypeName = t.simpleTypeName(); } TypeInfo ti = new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(), Converter.obtainClass(t.asClassDoc())); return ti; } @Override protected void made(Type t, TypeInfo ti) { if (t.asParameterizedType() != null) { ti.setTypeArguments(Converter.convertTypes(t.asParameterizedType().typeArguments())); } else if (t instanceof ClassDoc) { ti.setTypeArguments(Converter.convertTypes(((ClassDoc) t).typeParameters())); } else if (t.asTypeVariable() != null) { ti.setBounds(null, Converter.convertTypes((t.asTypeVariable().bounds()))); ti.setIsTypeVariable(true); } else if (t.asWildcardType() != null) { ti.setIsWildcard(true); ti.setBounds(Converter.convertTypes(t.asWildcardType().superBounds()), Converter .convertTypes(t.asWildcardType().extendsBounds())); } } @Override protected Object keyFor(Type t) { StringBuilder result = new StringBuilder(); result.append(t.getClass().getName()).append("/").append(t).append("/"); if (t.asParameterizedType() != null) { result.append(t.asParameterizedType()).append("/"); if (t.asParameterizedType().typeArguments() != null) { for (Type ty : t.asParameterizedType().typeArguments()) { result.append(ty).append("/"); } } } else { result.append("NoParameterizedType//"); } if (t.asTypeVariable() != null) { result.append(t.asTypeVariable()).append("/"); if (t.asTypeVariable().bounds() != null) { for (Type ty : t.asTypeVariable().bounds()) { result.append(ty).append("/"); } } } else { result.append("NoTypeVariable//"); } if (t.asWildcardType() != null) { result.append(t.asWildcardType()).append("/"); if (t.asWildcardType().superBounds() != null) { for (Type ty : t.asWildcardType().superBounds()) { result.append(ty).append("/"); } } if (t.asWildcardType().extendsBounds() != null) { for (Type ty : t.asWildcardType().extendsBounds()) { result.append(ty).append("/"); } } } else { result.append("NoWildCardType//"); } return result.toString(); } }; public static TypeInfo obtainTypeFromString(String type) { return mTypesFromString.obtain(type); } private static final Cache<String, TypeInfo> mTypesFromString = new Cache<String, TypeInfo>() { @Override protected TypeInfo make(String name) { return new TypeInfo(name); } }; private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) { int len = orig.length; AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len]; for (int i = 0; i < len; i++) { out[i] = Converter.obtainAnnotationInstance(orig[i]); } return out; } private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) { return mAnnotationInstances.obtain(o); } private static Cache<AnnotationDesc, AnnotationInstanceInfo> mAnnotationInstances = new Cache<AnnotationDesc, AnnotationInstanceInfo>() { @Override protected AnnotationInstanceInfo make(AnnotationDesc a) { ClassInfo annotationType; try { annotationType = Converter.obtainClass(a.annotationType()); } catch (ClassCastException e) { throw new IllegalArgumentException("Could not find annotation " + a.toString() + " on classpath.", e); } AnnotationDesc.ElementValuePair[] ev = a.elementValues(); AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length]; for (int i = 0; i < ev.length; i++) { elementValues[i] = obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element())); } return new AnnotationInstanceInfo(annotationType, elementValues); } }; private abstract static class Cache<K, V> { V obtain(K input) { if (input == null) { return null; } Object key = keyFor(input); V value = mCache.get(key); if (value == null) { value = make(input); if (value != null) { mCache.put(key, value); made(input, value); } } return value; } protected final HashMap<Object, V> mCache = new HashMap<Object, V>(); protected abstract V make(K input); protected void made(K input, V value) { } protected Object keyFor(K key) { return key; } Object[] all() { return null; } } // annotation values private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues = new HashMap<AnnotationValue, AnnotationValueInfo>(); private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>(); private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) { if (o == null) { return null; } AnnotationValueInfo v = mAnnotationValues.get(o); if (v != null) { return v; } v = new AnnotationValueInfo(element); mAnnotationValues.put(o, v); if (mAnnotationValuesNeedingInit != null) { mAnnotationValuesNeedingInit.add(o); } else { initAnnotationValue(o, v); } return v; } private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) { Object orig = o.value(); Object converted; if (orig instanceof Type) { // class literal converted = Converter.obtainType((Type) orig); } else if (orig instanceof FieldDoc) { // enum constant converted = Converter.obtainField((FieldDoc) orig); } else if (orig instanceof AnnotationDesc) { // annotation instance converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig); } else if (orig instanceof AnnotationValue[]) { AnnotationValue[] old = (AnnotationValue[]) orig; AnnotationValueInfo[] array = new AnnotationValueInfo[old.length]; for (int i = 0; i < array.length; i++) { array[i] = Converter.obtainAnnotationValue(old[i], null); } converted = array; } else { converted = orig; } v.init(converted); } private static void finishAnnotationValueInit() { while (mAnnotationValuesNeedingInit.size() > 0) { HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit; mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>(); for (AnnotationValue o : set) { AnnotationValueInfo v = mAnnotationValues.get(o); initAnnotationValue(o, v); } } mAnnotationValuesNeedingInit = null; } }